//
// Created by Administrator on 2021/11/22.
//

#ifndef LEARNREALTIMERENDERING_MESHUTILITY_H
#define LEARNREALTIMERENDERING_MESHUTILITY_H

#include "Geometry.hpp"

struct Vertex
{
public:
    Vec3f position;
    Vec3f normal;
    Vec2f uv;
};

class MeshUtility
{
public:
    /// 生成一个四边形面片
    static void GenerateQuad(Vertex*& vertices, GLuint*& indices, int& vertexCount, int& indexCount)
    {
        vertexCount = 4;
        indexCount = 6;
        vertices = new Vertex[vertexCount];
        vertices[0] = Vertex{
                Vec3f(-1.0f, 1.0f, 0.0f),
                Vec3f(0.0f, 0.0f, 1.0f),
                Vec2f(0.0f, 1.0f)
        };
        vertices[1] = Vertex {
                Vec3f(-1.0f, -1.0f, 0.0f),
                Vec3f(0.0f, 0.0f, 1.0f),
                Vec2f(0.0f, 0.0f)
        };
        vertices[2] = Vertex {
                Vec3f(1.0f, -1.0f, 0.0f),
                Vec3f(0.0f, 0.0f, 1.0f),
                Vec2f(1.0f, 0.0f)
        };
        vertices[3] = Vertex {
                Vec3f(1.0f, 1.0f, 0.0f),
                Vec3f(0.0f, 0.0f, 1.0f),
                Vec2f(1.0f, 1.0f)
        };

        indices = new GLuint[indexCount]{
            0, 1, 2,
            0, 2, 3
        };
    }

    /// 生成天空盒（和立方体的区别就是 因为假设摄像机是看的里面，所以要注意从里面来排序顶点
    static void GenerateSkybox(Vertex*& vertices, GLuint*& indices, int& vertexCount, int& indexCount)
    {
        // 对于天空盒来说这些是固定的
        vertexCount = 8;
        indexCount = 36;
        vertices = new Vertex[vertexCount];
        float extend = 1.0f;
        for(int i = 0; i < vertexCount; ++i)
        {
            Vertex vertex;
            float x = i % 4 > 1 ? extend : -extend;
            float y = (i % 4 < 1 || i % 4 > 2) ? extend : -extend;
            float z = i / 4 == 0 ? extend : -extend;
            vertex.position = Vec3f(x, y, z);
            vertices[i] = vertex;
        }

        indices = new GLuint[indexCount] {
            4, 5, 6,
            6, 7, 4,
            1, 5, 4,
            4, 0, 1,
            6, 2, 3,
            3, 7, 6,
            1, 0, 3,
            3, 2, 1,
            4, 7, 3,
            3, 0, 4,
            5, 1, 6,
            6, 1, 2
        };
    }

    /// 生成立方体网格
    static void GenerateCube(float L, Vertex*& vertices, GLuint*& indices, int& vertexCount, int& indexCount)
    {
        // 对于Cube来说这些是固定的
        vertexCount = 8;
        indexCount = 36;
        vertices = new Vertex[vertexCount];
        float extend = float(L) / 2.0f;
        for(int i = 0; i < vertexCount; ++i)
        {
            Vertex vertex;
            float x = i % 4 > 1 ? extend : -extend;
            float y = (i % 4 < 1 || i % 4 > 2) ? extend : -extend;
            float z = i / 4 == 0 ? extend : -extend;
            vertex.position = Vec3f(x, y, z);
            vertices[i] = vertex;
        }
        vertices[0].uv = Vec2f(0.0f, 1.0f);
        vertices[1].uv = Vec2f(0.0f, 0.0f);
        vertices[2].uv = Vec2f(1.0f, 0.0f);
        vertices[3].uv = Vec2f(1.0f, 1.0f);
        vertices[4].uv = Vec2f(1.0f, 1.0f);
        vertices[5].uv = Vec2f(1.0f, 0.0f);
        vertices[6].uv = Vec2f(0.0f, 0.0f);
        vertices[7].uv = Vec2f(0.0f, 1.0f);


        indices = new GLuint[indexCount]
                {0, 1, 2,
                 0, 2, 3,
                 3, 2, 6,
                 3, 6, 7,
                 7, 6, 5,
                 7, 5, 4,
                 4, 5, 1,
                 4, 1, 0,
                 0, 3, 7,
                 0, 7, 4,
                 1, 5, 6,
                 1, 6, 2};
    }

    /// 生成球面网格
    static void GenerateSphere(int N, float R, Vertex*& vertices, GLuint*& indices, int& vertexCount, int& indexCount)
    {
        // 1. 先将所有顶点计算出来并存到容器里
        // 注意，这里坐标系用的是z轴朝上，左手坐标系，之后需要乘一个WorldMatrix转换为右手坐标系，y轴朝上
        // -----------------------------------
        // 可事先计算出需要多少个顶点 垂直方向，N-1个顶点环，每个环有5个顶点，再加顶部底部两个
        vertexCount = N * (N - 1) + 2;
        vertices = new Vertex[vertexCount];
        // 顶部底部两个可以直接计算出来
        vertices[0].position = Vec3f(0.0f, 0.0f, R);
        vertices[0].normal = vertices[0].position.Norm();
        vertices[0].uv = Vec2f(0.0f, 1.0f);
        vertices[vertexCount - 1].position = Vec3f(0.0f, 0.0f, -R);
        vertices[vertexCount - 1].normal = vertices[vertexCount - 1].position.Norm();
        vertices[vertexCount - 1].uv = Vec2f(0.0f, 0.0f);
        // 开始自顶向下计算
        // 先计算垂直方向theta的增量
        float deltaTheta = PI / float(N);
        float deltaPhi = PI2 / float(N);
        // 计算水平和垂直方向上uv的增量
        float deltaUV = 1.0f / float(N);
        float x, y, z;
        for(int j = 1; j < N; ++j)
        {
            //theta += deltaTheta; //浮点数累加容易出现误差
            float theta = deltaTheta * float(j);
            // 注意uv是从左下角为(0, 0)
            float v = 1.0f - deltaUV * float(j);
            for(int i = 0; i < N; ++i)
            {
                float phi = deltaPhi * float(i);
                float u = deltaUV * float(i);
                // 转换为笛卡尔坐标
                x = sinf(theta) * cosf(phi) * R;
                y = sinf(theta) * sinf(phi) * R;
                z = cosf(theta) * R;
                vertices[1 + (j - 1) * N + i].position = Vec3f(x, y, z);
                vertices[1 + (j - 1) * N + i].normal = vertices[1 + (j - 1) * N + i].position.Norm();
                vertices[1 + (j - 1) * N + i].uv = Vec2f(u, v);
            }
        }

        // 2. 再按顺序从容器中取出顶点，构造三角形索引（逆时针）
        // -------------------------------------
        // 可以预先计算出需要多少索引 注意第一圈和最后一圈都是三角形，中间是四边形
        // 上N个+下N个+中间N-2层，每层N * 2个
        indices = new GLuint[(N + N + N * 2 * (N - 2)) * 3];
        // 从顶部顶点开始
        indexCount = 0;
        GLuint curStartVertexIndex = 0;
        for(int j = 0; j < N; ++j)
        {
            if(j == 0) // 第一圈
            {
                // 从第二个顶点开始
                curStartVertexIndex = 1;
                GLuint endVertexIndex = curStartVertexIndex + N;
                for(GLuint i = curStartVertexIndex; i < endVertexIndex; ++i)
                {
                    indices[indexCount++] = 0;
                    indices[indexCount++] = i;
                    indices[indexCount++] = i == endVertexIndex - 1 ? curStartVertexIndex : i + 1;
                }
                // 取最后一个
                curStartVertexIndex = indices[indexCount - 1];
            }
            else if(j == N - 1) // 最后一圈
            {
                GLuint endVertexIndex = curStartVertexIndex + N;
                for(GLuint i = curStartVertexIndex; i < endVertexIndex; ++i)
                {
                    indices[indexCount++] = i;
                    indices[indexCount++] = vertexCount - 1;
                    indices[indexCount++] = i == endVertexIndex - 1 ? curStartVertexIndex : i + 1;
                }
            }
            else // 中间
            {
                GLuint endVertexIndex = curStartVertexIndex + N;
                for(GLuint i = curStartVertexIndex; i < endVertexIndex; ++i)
                {
                    // 第一个三角形
                    indices[indexCount++] = i;
                    indices[indexCount++] = i + N;
                    indices[indexCount++] = i == endVertexIndex - 1 ? curStartVertexIndex + N : i + N + 1;
                    // 第二个三角形
                    indices[indexCount++] = i;
                    indices[indexCount++] = i == endVertexIndex - 1 ? curStartVertexIndex + N : i + N + 1;
                    indices[indexCount++] = i == endVertexIndex - 1 ? curStartVertexIndex : i + 1;
                }
                // 取倒数第二个
                curStartVertexIndex = indices[indexCount - 2];
            }
        }
    }
};

#endif //LEARNREALTIMERENDERING_MESHUTILITY_H
